﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SafeScript.MetaModel.Statements
{
    public class PropertyTailOperator : Operator
    {
        public Operator Operator;

        public override int GetPrecedence()
        {
            return this.Operator.GetPrecedence();
        }
    }

    public class PropertyOperator : PropertyTailOperator
    {
        private ITypeDef type;

        public string Identifier;
        /// <summary>
        /// The object denoted by the identifier
        /// </summary>
        public INamed Object;

        public override int GetPrecedence()
        {
            return 20;
        }

        public override ITypeDef ComputeType(ITypeDef goal, bool needsRef, ErrorReport report)
        {
            if (this.type != null)
                return this.type;

            // Do we access a type or an instance of a type?
            bool isInstance = false;
            ITypeDef t = this.Operator.ComputeType(null, false, report);
            // Some error?
            if (t == null)
                return null;

            if (t is InstanceType)
            {
                isInstance = true;
                t = (t as InstanceType).Type;
            }

            // It must not be a function type, for example
            if (!(t is Namespace))
            {
                report.Add(new ErrorEntry(this.Node) { Type = ErrorType.NotAnObject });
                return null;
            }

            // Find the identifier in the namespace
            Namespace bt = t as Namespace;
            object obj = ScopeResolver.FindObject(bt, this.Identifier, null, true);
            if (obj == null)
            {
                report.Add(new ErrorEntry(this.Node) { Info = this.Identifier, Type = ErrorType.UnknownIdentifier });
                return null;
            }

            this.Object = obj as INamed;
            System.Diagnostics.Debug.Assert(obj is INamed, "Found an object that has a name but does not implement INamed. Ooops");

            AtomIdentifier atom = new AtomIdentifier() { Identifier = this.Identifier, Node = this.Node, Object = obj };
            this.type = atom.ComputeType(null, needsRef, report);

            bool isIdentifierStatic = false;
            if (obj is MemberVariable && (obj as MemberVariable).IsStatic)
                isIdentifierStatic = true;
            else if (obj is Property && (obj as Property).IsStatic)
                isIdentifierStatic = true;
            else if (obj is MemberFunction && (obj as MemberFunction).IsStatic)
                isIdentifierStatic = true;
            else if (obj is MemberFunctionSet && (obj as MemberFunctionSet).Functions[0].IsStatic)
                isIdentifierStatic = true;
            else if (obj is AbstractType)
                isIdentifierStatic = true;
            else if (obj is Namespace)
                isIdentifierStatic = true;
            else if (obj is EnumItem)
                isIdentifierStatic = true;

            if (!isIdentifierStatic && !isInstance)
                report.Add(new ErrorEntry(this.Node) { Type = ErrorType.StaticCodeCannotAccessNonStaticMember, Info = this.Identifier });
            return this.type;
        }
    }
}
